import math
from collections import deque
from pathlib import Path

import addict
import toml

from ._typing import TeamName
from .utils import Point, atan, clip

config = addict.Dict(toml.load(Path(__file__).parent / "config.toml"))


class Soldier:
    """步兵类实现。

    :param x: 步兵的起始横坐标
    :type x: float
    :param y: 步兵的起始纵坐标
    :type y: float
    """

    id_counter: dict[str, int] = {"Red": 0, "Blue": 0}

    def __init__(self, x: float, y: float, team: TeamName) -> None:
        assert team in TeamName.__args__
        self.x = x
        self.y = y
        self._speed = config.soldier.speed
        self.speed = 0
        self.team = team
        self.x_max = config.map.width
        self.y_max = config.map.height
        self.hp = config.soldier.hp
        self.bullets = deque(
            [
                Bullet(self, config.bullet.damage)
                for _ in range(config.soldier.bullet_num)
            ],
        )
        self.consumed_bullets = deque[Bullet]()
        self.shoot_distance = config.soldier.shoot_distance
        self.uid = f"{team}->soldier:{Soldier.id_counter[team]}"
        Soldier.id_counter[team] += 1

    @property
    def alive(self) -> bool:
        """指示步兵是否存活。"""
        return self.hp > 1e-3

    @property
    def shootable(self) -> bool:
        """指示步兵是否还能射击。"""
        return self.alive and len(self.bullets) > 0

    def move(self, angle: float) -> None:
        r"""指定角度移动。

        :param angle: 朝哪个方向移动（弧度制 :math:`0\le angle\le 2\pi`）
        """
        assert 0 <= angle <= 2 * math.pi + 1e-3
        if self.alive is False:
            return
        self.speed = self._speed
        delta_x = self.speed * math.cos(angle)
        delta_y = self.speed * math.sin(angle)
        self.x = clip(self.x + delta_x, 0, self.x_max)
        self.y = clip(self.y + delta_y, 0, self.y_max)
        for b in self.bullets:
            b.x = self.x
            b.y = self.y

    def shoot(self, x: float, y: float) -> None:
        r"""朝指定角度射击。

        :param angle: 朝哪个方向射击（弧度制 :math:`0\le angle\le 2\pi`）
        """
        p = Point(x, y)
        angle = atan(self, p)
        if self.shootable is False:
            return
        self.speed = 0
        b = self.bullets.pop()
        b.x += self.shoot_distance * math.cos(angle)
        b.y += self.shoot_distance * math.sin(angle)
        b.x = clip(b.x, 0, self.x_max)
        b.y = clip(b.y, 0, self.y_max)
        self.consumed_bullets.append(b)


class Bullet:
    r"""子弹类实现。

    :param radius: 杀伤半径
    :param owner: 导弹的所有者
    :param damage: 中心伤害
    """

    def __init__(self, owner: Soldier, damage: float) -> None:
        self.owner = owner
        self.x = owner.x
        self.y = owner.y
        self.damage = damage
